#include "mapeador.h"

#include <fstream>
#include <memory>
#include <string.h>
#include <iostream>

Mapeador* Mapeador::mapeador = NULL;

Mapeador::Mapeador(){
	this->cargarMapeador();
	this->cargarRangos();
}

Mapeador* Mapeador::getInstance(){
	if (!mapeador){
		mapeador = new Mapeador();
	}

	return mapeador;
}

void Mapeador::cargarMapeador (){

	std::ifstream arch;

	std::string dirInput = getenv("INPUT_DIR");
	std::string archStrings = dirInput.append("/").append(DATA_FILE);


	arch.open(archStrings);
	std::string linea , clave, cadena;
	int posicionSeparador = 0;
	char* buffer = new char[TAM_LECTURA];

	if (!arch.good()){
		std::cout << "No se pudo abrir archivo " << archStrings << std::endl;
		return;
	}

	int i =1;

	vectorCadenas.push_back("");

	while ( !arch.eof()){
		memset (buffer, '\0', TAM_LECTURA); // limpio buffer
		arch.getline(buffer, TAM_LECTURA); // leo una linea
		linea = buffer;
		posicionSeparador = 0;

		posicionSeparador = linea.find(CARACTER_SEPARADOR , 0);
		clave = linea.substr(0, posicionSeparador);
		cadena = linea.substr(posicionSeparador+1, linea.length()-posicionSeparador);

		if(atoi(clave.c_str())==i){
			vectorCadenas.push_back(cadena);
			i++;
		} else {
			while(i < atoi(clave.c_str())){
				vectorCadenas.push_back("");
				i++;
			}

			vectorCadenas.push_back(cadena);
			i++;
		}

		cadena.clear();
		clave.clear();
		linea.clear();
	}

	arch.close();
}

char* Mapeador::mapIdToString(u_int32_t id){


	if (id < vectorCadenas.size()){
		return (char*)vectorCadenas[id].c_str();
	} else {
		return (char*)"";
	}
}

u_int32_t Mapeador::mapStringToId(std::string id){

	if (id.compare("") == 0 or id.compare("\n") == 0){
		return 0;
	}

	u_int32_t i;
	for ( i=1; i < this->vectorCadenas.size(); i++){
		if(id.compare(this->vectorCadenas[i]) == 0){
			return i;
		}
	}

	// si no pudo mapear de la entrada inicial, lo cargo
	vectorCadenas.push_back(id);

	u_int32_t idMap = (u_int32_t)( i - 1);
	std::string dirInput = getenv("INPUT_DIR");
	std::string archivoSalida = dirInput.append("/").append(DATA_FILE);

	std::ofstream arch;
	arch.open(archivoSalida, std::ios::app);
	if (!arch.good()){
		std::cout << "No se pudo abrir archivo " << archivoSalida << std::endl;
	}
	arch << idMap <<"|"<<id<< std::endl;
	arch.close();

	return idMap;
}

void Mapeador::cargarRangos(){
	std::ifstream arch;
	unsigned char idGenerado = 0;
	std::string dirInput = getenv("INPUT_DIR");
	std::string archivoAprocesar = PATH_RANGOS;
	std::string archStrings = dirInput.append("/").append(archivoAprocesar);

	arch.open(archStrings);
	std::string rangoLeido;
	if (!arch.good()){
		std::cout << "No se pudo abrir archivo" << archStrings << std::endl;
		return;
	}
	char* buffer = new char[TAM_LECTURA];
	while ( !arch.eof()){
		memset (buffer, '\0', TAM_LECTURA); // limpio buffer
		arch.getline(buffer, TAM_LECTURA); // leo una linea
		rangoLeido = buffer;
		this->mapaRangos.insert ( rango_mapa ( rangoLeido , idGenerado) ); // para generar id
		this->mapaRangosARecuperar.insert (rango_mapa_a_recuperar (idGenerado, rangoLeido)); // para recuperar id
		++idGenerado;
		rangoLeido.clear();
	}
	arch.close();
}

u_int32_t Mapeador::convertirRango (unsigned int anio, unsigned char mes, unsigned  char dia, std::string rango){
	u_int32_t idGenerado;
	unsigned char byte;
	unsigned char anioNormalizado = anio - ANIO_INICIAL;
	std::map<std::string, unsigned char>::iterator it = this->mapaRangos.find(rango);
	unsigned char rangoNormalizado = it->second;
	unsigned char* buffer = new unsigned char[4];
	memset (buffer , '\0' , sizeof (char) * 4);

	// guardo anio
	(*buffer) = anioNormalizado;
	++buffer;

	// guardo mes
	mes <<=4;
	(*buffer) = mes;

	// guardo dia
	byte = dia;
	byte >>=1;
	(*buffer) = (*buffer) | byte; // se esta guardando dia en el segundo byte
	byte = dia;
	byte <<=7;
	++buffer;
	(*buffer) = (*buffer) | byte;  // se esta guardando el ultimo bit de dia en el tercer byte

	// guardo
	byte = rangoNormalizado;
	byte <<=1;
	(*buffer) = (*buffer) | byte;

	buffer -= 2;

	memcpy(&idGenerado, buffer , sizeof(unsigned char) * 3);

	return idGenerado;
}

void Mapeador::recuperarRango (u_int32_t idARecuperar, unsigned int* anio, unsigned char* mes, unsigned char* dia, std::string* rango){
	unsigned char byte, byte2;
    unsigned char buffer[4];
    memset(buffer, '\0', 4);
    memcpy(buffer, &idARecuperar , sizeof (unsigned char) * 3);

    // recupero anio
    *anio = (int)buffer[0] + ANIO_INICIAL; // en el primer byte mas significativo viene el anio

	// guardo mes
    byte = buffer[1];
	byte >>=4; // solo uso 4bits para el mes
	*mes = byte;

	// guardo dia
	byte = buffer[1];
	byte <<=4;
	byte >>=3; // para dejar los ultimos 5bits el dia

	byte2 = buffer[2];
	byte2 >>= 7; // el primer bit del byte corresponde al dia

	*dia = byte | byte2; // se esta guardando

	// recupero rango
	byte2 = buffer[2];
	byte2 <<=1; // saco el bit que correponde al dia
	byte2 >>=2; // saco el bit de relleno y vuelvo una posicion por el paso anterior

	std::map<unsigned char,std::string>::iterator it = this->mapaRangosARecuperar.find(byte2); // busco por id de rango
	*rango = it->second;
}

void Mapeador::mapearFechaLeida(std::string& linea, unsigned int* anio, unsigned char* mes, unsigned char* dia, std::string* rango){
	int posicionSeparadorAnt = 0 , posicionSeparadorPost = 0;
    std::string buffer;
	//ej: 00:30 - 01:00 20/02/2011
	posicionSeparadorPost = linea.find(CARACTER_SEPARADOR_FECHA , posicionSeparadorAnt);
	posicionSeparadorPost -= 3; // Resto 3 xq " 20" antes del rango
	*rango = linea.substr(posicionSeparadorAnt, posicionSeparadorPost - posicionSeparadorAnt);

	posicionSeparadorAnt  = posicionSeparadorPost + 1; // me paro al principio de " 20"
	posicionSeparadorPost = posicionSeparadorAnt + 2; // me paro al final del campo dia
	buffer = linea.substr(posicionSeparadorAnt, posicionSeparadorPost - posicionSeparadorAnt);
	*dia = (unsigned char)atoi(buffer.c_str());

	posicionSeparadorAnt  = posicionSeparadorAnt + 3; // me paro al principio de mes
	posicionSeparadorPost = posicionSeparadorAnt + 2; // me paro al final del campo mes
	buffer = linea.substr(posicionSeparadorAnt, posicionSeparadorPost - posicionSeparadorAnt);
	*mes = (unsigned char)atoi(buffer.c_str());

	posicionSeparadorAnt  = posicionSeparadorAnt + 3; // me paro al principio de anio
	posicionSeparadorPost = posicionSeparadorAnt + 4; // me paro al final del campo anio
	buffer = linea.substr(posicionSeparadorAnt, posicionSeparadorPost - posicionSeparadorAnt);
	*anio = (unsigned int)atoi(buffer.c_str());

}
